package model.components.networks.virtual;

import java.util.ArrayList;
import java.util.List;

import model.components.links.link.Link;
import model.components.links.link.VirtualLink;
import model.components.networks.NetworkGraph;
import model.components.nodes.node.Node;
import model.components.nodes.node.virtual.VirtualMachine;
import model.components.nodes.node.virtual.VirtualRouter;
import model.components.nodes.node.virtual.VirtualSwitch;
import cern.jet.random.Exponential;
import cern.jet.random.Poisson;
import cern.jet.random.engine.DRand;
import edu.uci.ics.jung.graph.Graph;

public class VirtualNetworks
{
	public static final String NAME_PREFIX = VirtualNetwork.class.getSimpleName();
	public static final int NETWORKS_NUMBER = 1;
	
	public static final int WINDOW_TIME_UNITS_NUMBER = 0;
	public static final double POISSON_ARRIVES = 1;
	public static final double EXPONENTIAL_LIFETIME = 1;
	
	public static final int MIN_ROUTERS_NUMBER = 2;
	public static final int MAX_ROUTERS_NUMBER = 2;
	
	public static final int MIN_SERVERS_NUMBER =  0;
	public static final int MAX_SERVERS_NUMBER = 0;
	
	public static final int MIN_SWITCHES_NUMBER =  0;
	public static final int MAX_SWITCHES_NUMBER = 0;
	
	public static final int LINUX_PERCENTAGE = 100;
	public static final int WINDOWS_PERCENTAGE = 0;
	public static final int SOLARIS_PERCENTAGE = 0;
	public static final int ANDROID_PERCENTAGE = 0;
	
	public static final int MIN_CPU = 1;
	public static final int MAX_CPU = 1;
	
	public static final int MIN_RAM = 1;
	public static final int MAX_RAM = 1;
	
	public static final int MIN_STORAGE = 1;
	public static final int MAX_STORAGE = 1;
	
	public static final int MIN_BW = 1;
	public static final int MAX_BW = 2;
	
	private List< VirtualNetwork > virtualNetworks;
	public List< VirtualNetwork > getVirtualNetworks(){ return virtualNetworks; }
	public void setVirtualNetworks( List< VirtualNetwork > virtualNetworks ){ this.virtualNetworks = virtualNetworks; }
	
	public static final int MIN_NODES_NUMBER = 8;
	public static final int MAX_NODES_NUMBER = 8;
	public static final int LINKS_NUMBER_PERCENTAGE = 25;
	public static final int MIN_LINKS_NUMBER_RANGE =  1;
	public static final int MAX_LINKS_NUMBER_RANGE = 10;
	
	public VirtualNetworks()
	{
		List< VirtualNetwork > virtualNetworks = this.virtualNetworks( NAME_PREFIX, NETWORKS_NUMBER, WINDOW_TIME_UNITS_NUMBER, POISSON_ARRIVES, EXPONENTIAL_LIFETIME, MIN_ROUTERS_NUMBER, MAX_ROUTERS_NUMBER, MIN_SWITCHES_NUMBER, MAX_SWITCHES_NUMBER, MIN_SERVERS_NUMBER, MAX_SERVERS_NUMBER, LINUX_PERCENTAGE, WINDOWS_PERCENTAGE, SOLARIS_PERCENTAGE, ANDROID_PERCENTAGE, MIN_CPU, MAX_CPU, MIN_RAM, MAX_RAM, MIN_STORAGE, MAX_STORAGE, MIN_BW, MAX_BW );
		this.setVirtualNetworks( virtualNetworks );
	}
	
	public VirtualNetworks( String namePrefix, int networksNumber, int windowTimeUnitsNumber, double poissonArrives, double exponentialLifetime, int minRoutersNumber, int maxRoutersNumber, int minSwitchesNumber, int maxSwitchesNumber, int minServersNumber, int maxServersNumber, int linuxPercentage, int windowsPercentage, int solarisPercentage, int androidPercentage, int minCPU, int maxCPU, int minRAM, int maxRAM, int minStorage, int maxStorage, int minBW, int maxBW )
	{
		List< VirtualNetwork > virtualNetworks = this.virtualNetworks( namePrefix, networksNumber, windowTimeUnitsNumber, poissonArrives, exponentialLifetime, minRoutersNumber, maxRoutersNumber, minSwitchesNumber, maxSwitchesNumber, minServersNumber, maxServersNumber, linuxPercentage, windowsPercentage, solarisPercentage, androidPercentage, minCPU, maxCPU, minRAM, maxRAM, minStorage, maxStorage, minBW, maxBW );
		this.setVirtualNetworks( virtualNetworks );
	}
	
	private List< VirtualNetwork > virtualNetworks( String namePrefix, int networksNumber, int windowTimeUnitsNumber, double poissonArrives, double exponentialLifetime, int minRoutersNumber, int maxRoutersNumber, int minSwitchesNumber, int maxSwitchesNumber, int minServersNumber, int maxServersNumber, int linuxPercentage, int windowsPercentage, int solarisPercentage, int androidPercentage, int minCPU, int maxCPU, int minRAM, int maxRAM, int minStorage, int maxStorage, int minBW, int maxBW )
	{
		List< VirtualNetwork > virtualNetworks = new ArrayList< VirtualNetwork >();
		
		Poisson poisson = new Poisson( poissonArrives, new DRand() );
		Exponential exponential = new Exponential( 1 / exponentialLifetime, new DRand() );
		int networkNumber = 0;
		int startingDate = 0;
		
		while( networkNumber < networksNumber )
		{
			int virtualNetworkRequestsNumber = poisson.nextInt();
			
			for( int i = 0; i < virtualNetworkRequestsNumber; i++ )
				if( networkNumber < networksNumber )
				{
					//int endingDate = startingDate + exponential.nextInt();
					int endingDate = 1;
					
					String nameNetwork = namePrefix + networkNumber;
					Graph< Node, Link > graph = new NetworkGraph();
					int nodeCount = 0;
					
					int virtualRoutersNumber = this.randomMinMaxNumber( minRoutersNumber, maxRoutersNumber );
					List< VirtualRouter > virtualRouters = new ArrayList< VirtualRouter >();
					
					for( int j = 0; j < virtualRoutersNumber; j++ )
					{
						String os = this.randomOS( linuxPercentage, windowsPercentage, solarisPercentage, androidPercentage );
						int cpu = this.randomMinMaxNumber( minCPU, maxCPU );
						int ram = this.randomMinMaxNumber( minRAM, maxRAM );
						VirtualRouter sdnRouter = new VirtualRouter( nameNetwork, startingDate, endingDate, nodeCount++, os, cpu, ram );
						virtualRouters.add( sdnRouter );
						graph.addVertex( sdnRouter );
					}
					
					int virtualSwitchesNumber = this.randomMinMaxNumber( minSwitchesNumber, maxSwitchesNumber );
					List< VirtualSwitch > virtualSwitches = new ArrayList< VirtualSwitch >();
					
					for( int j = 0; j < virtualSwitchesNumber; j++ )
					{
						String os = this.randomOS( linuxPercentage, windowsPercentage, solarisPercentage, androidPercentage );
						int cpu = this.randomMinMaxNumber( minCPU, maxCPU );
						int ram = this.randomMinMaxNumber( minRAM, maxRAM );
						VirtualSwitch virtualSwitch = new VirtualSwitch( nameNetwork, startingDate, endingDate, nodeCount++, os, cpu, ram );
						virtualSwitches.add( virtualSwitch );
						graph.addVertex( virtualSwitch );
					}
					
					int virtualMachinesNumber = this.randomMinMaxNumber( minServersNumber, maxServersNumber );
					List< VirtualMachine > virtualMachines = new ArrayList< VirtualMachine >();
					
					for( int j = 0; j < virtualMachinesNumber; j++ )
					{
						String os = this.randomOS( linuxPercentage, windowsPercentage, solarisPercentage, androidPercentage );
						int cpu = this.randomMinMaxNumber( minCPU, maxCPU );
						int ram = this.randomMinMaxNumber( minRAM, maxRAM );
						int storage = this.randomMinMaxNumber( minStorage, maxStorage );
						VirtualMachine virtualMachine = new VirtualMachine( nameNetwork, startingDate, endingDate, nodeCount++, os, cpu, ram, storage );
						virtualMachines.add( virtualMachine );
						graph.addVertex( virtualMachine );
					}
													
					int linkCount = 0;
					
					/*for( VirtualRouter virtualRouter: virtualRouters )
					{
						List< VirtualSwitch > randomVirtualSwitches = this.randomVirtualSwitches( virtualSwitches );
						
						for( VirtualSwitch randomVirtualSwitch: randomVirtualSwitches )
						{
							int bw = this.randomMinMaxNumber( minBW, maxBW );
							VirtualLink virtualLink = new VirtualLink( nameNetwork, startingDate, endingDate, linkCount++, bw );
							graph.addEdge( virtualLink, virtualRouter, randomVirtualSwitch );
						}
					}*/
					
					//
					for( VirtualRouter virtualRouter: virtualRouters )
						for( VirtualRouter otherVirtualRouter: virtualRouters )
							if( virtualRouter.getNumber() < otherVirtualRouter.getNumber() )
							{
								int bw = this.randomMinMaxNumber( minBW, maxBW );
								VirtualLink virtualLink = new VirtualLink( nameNetwork, startingDate, endingDate, linkCount++, bw );
								graph.addEdge( virtualLink, virtualRouter, otherVirtualRouter );
							}
					//
					
					for( VirtualSwitch virtualSwitch: virtualSwitches )
						for( VirtualSwitch otherVirtualSwitch: virtualSwitches )
							if( virtualSwitch.getNumber() < otherVirtualSwitch.getNumber() )
							{
								int bw = this.randomMinMaxNumber( minBW, maxBW );
								VirtualLink virtualLink = new VirtualLink( nameNetwork, startingDate, endingDate, linkCount++, bw );
								graph.addEdge( virtualLink, virtualSwitch, otherVirtualSwitch );
							}
									
					for( VirtualMachine virtualMachine: virtualMachines )
					{	
						int bw = this.randomMinMaxNumber( minBW, maxBW );
						VirtualLink virtualLink = new VirtualLink( nameNetwork, startingDate, endingDate, linkCount++, bw );
						VirtualSwitch virtualSwitch = virtualSwitches.get( this.randomMinMaxNumber( 0, virtualSwitches.size() - 1 ) );
						graph.addEdge( virtualLink, virtualMachine, virtualSwitch );
					}
										
					virtualNetworks.add( new VirtualNetwork( namePrefix, networkNumber++, graph, startingDate, endingDate ) );
				}
			
			startingDate += windowTimeUnitsNumber;
		}
		
		return virtualNetworks;
	}
	private String randomOS( int linuxPercentage, int windowsPercentage, int solarisPercentage, int androidPercentage )
	{
		double randomOS = 100 * Math.random();
		
		if( 0 <= randomOS && randomOS < linuxPercentage )
			return Node.LINUX_OS;
		
		else if( linuxPercentage <= randomOS && randomOS < linuxPercentage + windowsPercentage )
			return Node.WINDOWS_OS;
		
		else if( linuxPercentage + windowsPercentage <= randomOS && randomOS < linuxPercentage + windowsPercentage + solarisPercentage )
			return Node.SOLARIS_OS;
		
		else if( linuxPercentage + windowsPercentage + solarisPercentage <= randomOS && randomOS < 100 )
			return Node.ANDROID_OS;
		
		return null;
	}
	private int randomMinMaxNumber( int minNumber, int maxNumber )
	{
		return minNumber + (int) ( Math.random() * ( maxNumber - minNumber + 1 ) );
	}
	private List< VirtualSwitch > randomVirtualSwitches( List< VirtualSwitch > virtualSwitches )
	{
		List< VirtualSwitch > randomVirtualSwitches = new ArrayList< VirtualSwitch >();
		List< Integer > selectedIndexes = new ArrayList< Integer >();
		int randomVirtualSwitchesNumber = this.randomMinMaxNumber( 1, virtualSwitches.size() );
		int numberOk = 0;
		
		while( numberOk < randomVirtualSwitchesNumber )
		{
			int randomIndex = this.randomMinMaxNumber( 0, virtualSwitches.size() - 1 );
			boolean isSelected = false;
			
			for( int selectedIndex: selectedIndexes )
				if( selectedIndex == randomIndex )
				{
					isSelected = true;
					break;
				}
			
			if( !isSelected )
			{
				selectedIndexes.add( randomIndex );
				randomVirtualSwitches.add( virtualSwitches.get( randomIndex ) );
				numberOk++;
			}
		}
		
		return randomVirtualSwitches;
	}

}
